package com.tool.shiro.config;

import at.pollux.thymeleaf.shiro.dialect.ShiroDialect;
import com.tool.shiro.pojo.ShiroResource;
import com.tool.shiro.realm.AdminRealm;
import com.tool.shiro.realm.CustomizeModularRealmAuthenticator;
import com.tool.shiro.realm.ShiroAdminRealm;
import com.tool.shiro.realm.UserRealm;
import com.tool.shiro.service.ShiroResourceService;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.authc.pam.AtLeastOneSuccessfulStrategy;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.*;

// shiro配置类
@Component(value = "shiroConfig")
@ConfigurationProperties(prefix = "shiro")
@Configuration
public class ShiroConfig {


    @Value("${shiro.index}")
    private String index;

    /**
     * 主页地址
     */
    public String getConfigIndex() {
        return index;
    }

    // 为了在HTML标签中使用shiro标签
    @Bean(name = "shiroDialect")
    public ShiroDialect shiroDialect() {
        return new ShiroDialect();
    }

    @Resource
    private ShiroResourceService shiroResourceService;

    @Primary
    @Bean("shiroFilter")
    public ShiroFilterFactoryBean getShiroFilterFactoryBean() {
        //@Qualifier("securityManager") DefaultWebSecurityManager defaultWebSecurityManager
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(getDefaultWebSecurityManager());
        // 配置系统 公共资源 和 受限资源
        // 注意使用LinkedHashMap
        Map<String, String> map = new LinkedHashMap<>();
        // 从数据库获取的权限
        List<ShiroResource> shiroResources = shiroResourceService.getAllShiroResourceInfo();
        for (ShiroResource shiroResource : shiroResources) {
            map.put(shiroResource.getValue(), shiroResource.getControl());
        }
        map.put("/druid/**", "anon");
        map.put("/admin/updateShiroResource", "authc");
        map.put("/**", "authc");
        // 默认认证[登录]页面
        shiroFilterFactoryBean.setLoginUrl("/loginPage");
        // 添加链式过滤map对象
        shiroFilterFactoryBean.setFilterChainDefinitionMap(map);
        return shiroFilterFactoryBean;
    }

    /**
     * 创建安全管理器
     */
    @Bean(name = "securityManager")
    public DefaultWebSecurityManager getDefaultWebSecurityManager(){
        //@Qualifier("shiroAdminRealm") Realm realm
        DefaultWebSecurityManager defaultWebSecurityManager = new DefaultWebSecurityManager();
        // 设置realm
        defaultWebSecurityManager.setAuthenticator(modularRealmAuthenticator());
        List<Realm> realms = new ArrayList<>();
        // 添加Realm
        realms.add(shiroAdminRealm());
        realms.add(userRealm());
        realms.add(adminRealm());
        //给安全管理器设置自定义realm
        defaultWebSecurityManager.setRealms(realms);
        // 可以在此处配置所有Realm的缓存管理
        //defaultWebSecurityManager.setCacheManager();
        return defaultWebSecurityManager;
    }

    /**
     * 系统自带的Realm管理，主要针对多realm
     * */
    @Bean
    public ModularRealmAuthenticator modularRealmAuthenticator() {
        //重写的CustomizeModularRealmAuthenticator
        CustomizeModularRealmAuthenticator customizeModularRealmAuthenticator = new CustomizeModularRealmAuthenticator();
        customizeModularRealmAuthenticator.setAuthenticationStrategy(new AtLeastOneSuccessfulStrategy());
        return customizeModularRealmAuthenticator;
    }

    /**
     * shiro工具管理员realm
     */
    @Bean(name = "shiroAdminRealm")
    public ShiroAdminRealm shiroAdminRealm(){
        ShiroAdminRealm shiroAdminRealm = new ShiroAdminRealm();
        //修改凭证校验匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //设置散列次数
        credentialsMatcher.setHashIterations(1024);
        shiroAdminRealm.setCredentialsMatcher(credentialsMatcher);

        //开启缓存管理 [默认缓存为EhCache]
        // 设置缓存管理为Redis缓存
        //shiroAdminRealm.setCacheManager(new RedisCacheManager());
        //开启全局缓存
        shiroAdminRealm.setCachingEnabled(true);
        //开启认证缓存
        shiroAdminRealm.setAuthenticationCachingEnabled(true);
        // 使用Redis缓存时设置认证缓存名称
        //shiroAdminRealm.setAuthenticationCacheName("authenticationCache");
        //开启授权缓存
        shiroAdminRealm.setAuthorizationCachingEnabled(true);
        // 使用Redis缓存时设置授权缓存名称
        //shiroAdminRealm.setAuthorizationCacheName("authorizationCache");

        return shiroAdminRealm;
    }


    /**
     * 自定义用户realm
     * 注意: 和Realm中一样，当定制化程度较高时需修改
     */
    @Bean(name = "userRealm")
        public UserRealm userRealm(){
        UserRealm userRealm = new UserRealm();
        //修改凭证校验匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //设置散列次数
        credentialsMatcher.setHashIterations(1024);
        userRealm.setCredentialsMatcher(credentialsMatcher);
        return userRealm;
    }


    /**
     * 自定义管理员realm
     * 注意: 和Realm中一样，当定制化程度较高时需修改
     */
    @Bean(name = "adminRealm")
    public AdminRealm adminRealm(){
        AdminRealm adminRealm = new AdminRealm();
        //修改凭证校验匹配器
        HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
        //设置加密算法为md5
        credentialsMatcher.setHashAlgorithmName("MD5");
        //设置散列次数
        credentialsMatcher.setHashIterations(1024);
        adminRealm.setCredentialsMatcher(credentialsMatcher);
        return adminRealm;
    }

    // TODO: @RequiresRoles和@RequiresPermissions等注解暂无法使用反射获取并动态配置，因此采用在接口中定义，
    //   shiro工具在前端可配置用户角色和权限的方式实现动态用户角色权限，不实现动态接口注解，也不安全（后期可尝试）
    /**
     * 开启Shiro的注解
     * (如@RequiresRoles,@RequiresPermissions),需借助SpringAOP扫描使用Shiro注解的类,
     * 并在必要时进行安全逻辑验证 * 配置以下两个bean(DefaultAdvisorAutoProxyCreator(可选)
     * 和AuthorizationAttributeSourceAdvisor)即可实现此功能
     */
    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator proxyCreator = new DefaultAdvisorAutoProxyCreator();
        proxyCreator.setProxyTargetClass(true);
        return proxyCreator;
    }
    /**
     * 开启 shiro aop注解支持
     */
    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor() {
        AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
        advisor.setSecurityManager(getDefaultWebSecurityManager());
        return advisor;
    }


}


